package com.codingfly.service.impl;

import cn.hutool.core.convert.Convert;
import com.codingfly.core.exception.BizException;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Service
public class CommonServiceImpl {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Autowired
    private NamedParameterJdbcTemplate namedJdbcTemplate;

    public Integer queryMaxOrderNoInt(String tableName) {
        String sql = "SELECT max(order_no) FROM "+tableName;
        return jdbcTemplate.queryForObject(sql, Integer.class);
    }

    public Integer queryNextOrderNoInt(String tableName) {
        Integer maxOrderNo = queryMaxOrderNoInt(tableName);
        return maxOrderNo!=null? (maxOrderNo+1):1;
    }

    public Long queryMaxOrderNoLong(String tableName) {
        String sql = "SELECT max(order_no) FROM "+tableName;
        return jdbcTemplate.queryForObject(sql, Long.class);
    }

    public Long queryNextOrderNoLong(String tableName) {
        Long maxOrderNo = queryMaxOrderNoLong(tableName);
        return maxOrderNo!=null? (maxOrderNo+1L):1L;
    }

    public void upOrderNoWithoutParent(String tableName, Serializable id) {
        Map<String, Object> map = Optional.ofNullable(findMapById(tableName, id)).orElseThrow(()->new BizException("该记录不存在或者已删除"));
        if (map.get("order_no")!=null) {
            OrderItemParent greater = convertOrderItemParent(map);
            String sql = "SELECT id, order_no FROM " + tableName + " WHERE order_no<:orderNo ORDER BY order_no DESC LIMIT 1 ";
            List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, greater.getOrderNo());
            exchangeOrderNo(tableName, greater, ObjectUtils.isEmpty(list)? null:list.get(0));
        }
    }

    public void downOrderNoWithoutParent(String tableName, Serializable id) {
        Map<String, Object> map = Optional.ofNullable(findMapById(tableName, id)).orElseThrow(()->new BizException("该记录不存在或者已删除"));
        if (map.get("order_no")!=null) {
            OrderItemParent less = convertOrderItemParent(map);
            String sql = "SELECT id, order_no FROM " + tableName + " WHERE order_no>:orderNo ORDER BY order_no ASC LIMIT 1";
            List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, less.getOrderNo());
            exchangeOrderNo(tableName, less, ObjectUtils.isEmpty(list)? null:list.get(0));
        }
    }

    public void upOrderNoHasParent(String tableName, Serializable id) {
        Map<String, Object> map = Optional.ofNullable(findMapById(tableName, id)).orElseThrow(()->new BizException("该记录不存在或者已删除"));
        if (map.get("order_no")!=null) {
            OrderItemParent greater = convertOrderItemParent(map);
            Map<String, Object> params = new HashMap() {{
               put("orderNo", greater.getOrderNo());
               put("parentId", greater.getParentId());
            }};
            String sql = "SELECT id, order_no FROM " + tableName + " WHERE order_no<:orderNo ";
            sql += (greater.getParentId()==null)? " AND parent_id IS NULL ":" AND parent_id=:parentId ";
            sql += " ORDER BY order_no DESC LIMIT 1 ";
            List<Map<String, Object>> list = namedJdbcTemplate.queryForList(sql, params);
            exchangeOrderNo(tableName, greater, ObjectUtils.isEmpty(list)? null:list.get(0));
        }
    }

    public void downOrderNoHasParent(String tableName, Serializable id) {
        Map<String, Object> map = Optional.ofNullable(findMapById(tableName, id)).orElseThrow(()->new BizException("该记录不存在或者已删除"));
        if (map.get("order_no")!=null) {
            OrderItemParent less = convertOrderItemParent(map);
            Map<String, Object> params = new HashMap() {{
                put("orderNo", less.getOrderNo());
                put("parentId", less.getParentId());
            }};
            String sql = "SELECT id, order_no FROM " + tableName + " WHERE order_no>:orderNo ";
            sql += (less.getParentId()==null)? " AND parent_id IS NULL ":" AND parent_id=:parentId ";
            sql +=" ORDER BY order_no ASC LIMIT 1";
            List<Map<String, Object>> list = namedJdbcTemplate.queryForList(sql, params);
            exchangeOrderNo(tableName, less, ObjectUtils.isEmpty(list)? null:list.get(0));
        }
    }

    private void exchangeOrderNo(String tableName, OrderItemParent o1, Map map) {
        if (map!=null && map.get("order_no")!=null) {
            OrderItemParent greater = convertOrderItemParent(map);
            exchangeOrderNo(tableName, o1, greater);
        }
    }

    public Map<String, Object> findMapById(String tableName, Serializable id) {
        String sql = "SELECT order_no, parent_id, id FROM "+tableName+" WHERE id=?";
        List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, id);
        return ObjectUtils.isEmpty(list)? null:list.get(0);
    }

    public void exchangeOrderNo(String tableName, OrderItemParent o1, OrderItemParent o2) {
        String sql = "UPDATE "+tableName+" SET order_no=:orderNo WHERE id=:id";
        Map<String, Object> params = new HashMap() {{
            put("id", o1.getId());
            put("orderNo", o2.getOrderNo());
        }};
        namedJdbcTemplate.update(sql, params);
        params = new HashMap() {{
            put("id", o2.getId());
            put("orderNo", o1.getOrderNo());
        }};
        namedJdbcTemplate.update(sql, params);
    }

    public OrderItemParent convertOrderItemParent(Map<String, Object> map) {
        OrderItemParent orderItemParent = new OrderItemParent();
        orderItemParent.setId(map.get("id")!=null? Convert.toLong(map.get("id")):null);
        orderItemParent.setParentId(map.get("parent_id")!=null? Convert.toLong(map.get("parent_id")):null);
        orderItemParent.setOrderNo(map.get("order_no")!=null? Convert.toLong(map.get("order_no")):null);
        return orderItemParent;
    }

    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public static class OrderItemParent {
        private Long id;
        private Long parentId;
        private Long orderNo;
    }

}
